/*
	ACDM_keyboard.c

	contains keyboard control functions

	Author: Aidan Goddard 14/11/13
*/

#include"../../klib/headers/ktypes.h"
#include"../../klib/headers/klib_system_resources.h"
#include"../../klib/headers/klib_proc_threads.h"
#include"../../klib/headers/klib_keyboard_buffer.h"
#include"../../kernel/headers/types.h"

#include"ACDM_keyboard.h"
#include"scan_codes.h"

#include"../../setup/headers/printf.h"

extern void ACDMSpinEnter(uint8_t *lock);
extern void ACDMSpinLeave(uint8_t *lock);


////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// formatted input functions & control

// buffer waiting thread tables
volatile uint64_t get_ascii_waiting_TIDs[512];
uint8_t get_ascii_locks[512];

// buffers
volatile char ascii_buffers[512];

// blocks until ascii char is available. will return 0 if other thread is already waiting
char KeyboardGetASCIIChar(int buffer)
{
	// aquire lock
	ACDMSpinEnter(&get_ascii_locks[buffer]);

	// return value
	char to_return = 0;

	// check if thread is already waiting on input
	if(get_ascii_waiting_TIDs[buffer] == 0)
	{
		// get the current thread ID
		uint64_t TID = __KLIB__RCGetTID();
		get_ascii_waiting_TIDs[buffer] = TID;

		// check the buffer isn't empty
		if(ascii_buffers[buffer] == 0)
		{
			// buffer is empty, need to wait for it to be non-empty
			// release lock
			ACDMSpinLeave(&get_ascii_locks[buffer]);

			// sleep
			__KLIB__WaitThisThread();

			// aquire lock
			ACDMSpinEnter(&get_ascii_locks[buffer]);
		}

		// get the char value
		to_return = ascii_buffers[buffer];
		ascii_buffers[buffer] = 0;

		// clear the waiting thread value
		get_ascii_waiting_TIDs[buffer] = 0;
	}

	// release lock
	ACDMSpinLeave(&get_ascii_locks[buffer]);

	// finished
	return to_return;
}

// function to clear a char buffer before fetching chars
void KeyboardClearASCIIChar(int buffer)
{
	// aquire lock
	ACDMSpinEnter(&get_ascii_locks[buffer]);

	// clear the buffer
	ascii_buffers[buffer] = 0;

	// release lock
	ACDMSpinLeave(&get_ascii_locks[buffer]);

	// finished
	return;
}


// called by the display manager process. inserts a formatted ascii char into the active buffer's char buffer
void KeyboardPutASCIIChar(int buffer, char c)
{
	// get lock
	ACDMSpinEnter(&get_ascii_locks[buffer]);

	// copy the char into the buffer
	ascii_buffers[buffer] = c;

	// check if a thread is sleeping on this buffer
	if(get_ascii_waiting_TIDs[buffer] != 0)
	{
		// wake that thread
		__KLIB__WakeThreadEx(get_ascii_waiting_TIDs[buffer]);
	}

	// release lock
	ACDMSpinLeave(&get_ascii_locks[buffer]);

	// finished
	return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// system keystate map

// keystate map
// a value of 0 indicates the key is currently not pressed
// a value larger than 0 indicates the number of consecutive frames the key has been pressed for
static uint8_t key_states[256];

// lock states
static int caps_lock = 0;
static int num_lock = 0;
static int scroll_lock = 0;
static int shift = 0;
static int F1 = 0;

// super key states
static int super_pressed = 0;
static int super_do_not_signal = 0;	// if 1, do not signal super key released when the key is released
static int a_left = 0;
static int a_right = 0;
static int a_up = 0;
static int a_down = 0;
static int c_down = 0;
static int l_down = 0;

// function to update internal keyboard state from the PS/2 buffer
// returns 64-bit system key action bitmap to ACDM:

// bit 0	- F1 key pressed (HELP)
// bit 1	- super key released
// bit 2	- super + left key
// bit 3	- super + right key
// bit 4	- super + up key
// bit 5	- super + down key
// bit 6 	- pause key
// bit 7	- break key
// bit 8	- print screen
// bit 9	- super + c (close current window)
// bit 10	- super + l (lock screen)

// bit 57	- super held
// bit 58	- caps lock off
// bit 59	- num lock off
// bit 60	- scroll lock off
// bit 61	- caps lock on
// bit 62	- num lock on
// bit 63	- scroll lock on

static uint64_t PS2_buffer[256];
uint64_t UpdateKeymapFromPS2()
{
	// get the PS2 scan codes for this frame
	int scan_count = 0;
	int kb_error = __KLIB__KBBufferRead(&scan_count, PS2_buffer);
	uint64_t to_return = 0;

	// for each scan code produced
	int i;
	for(i = 0; i < scan_count; i++)
	{
		// get the code
		uint64_t code = PS2_buffer[i];

		// check if is one of the special codes (PAUSE, BREAK, PRINT SCREEN PRESS, PRINT SCREEN RELEASE)
		if(code == 0xe02ae037)	// PRINT SCREEN PRESS
		{
			to_return = 0x100;
		}
		else if(code == 0xe0b7e0aa) // PRINT SCREEN RELEASE
		{
		}
		else if(code == 0xe11d45e19dc5) // PAUSE
		{
			to_return = 0x40;
		}
		else if(code == 0xe046e0c6) // BREAK
		{
			to_return = 0x80;
		}
		else // otherwise is normal scan code
		{
			// get offset into table
			uint8_t offset = code & 0x7f;	// mask all but bottom 7 bits

			// get the usb/system table offset number
			uint8_t sys_offset = 0;
			if((code & 0xff00) == 0xe000)
			{
				sys_offset = PS2_LONG_USB[offset];
			}
			else
			{
				sys_offset = PS2_SHORT_USB[offset];
			}

			// check if is make or break
			if((code & 0x80) > 0)
			{
				// is a break, clear the key in the system map
				key_states[sys_offset] = 0;
			}
			else
			{
				// is a make. increase the count at the key's state
				if(key_states[sys_offset] > 250)
				{
					key_states[sys_offset] = 1;
				}
				else
				{
					key_states[sys_offset]++;
				}
			}

			//printf("\n%x %x", code, sys_offset);
		}

	}

	// increase count for keys that are non-zero
	for(i = 0; i < 128; i++)
	{
		if(key_states[i] > 0)
		{

			if(key_states[i] > 250)
			{
				key_states[i] = 1;
			}
			else
			{
				key_states[i]++;
			}
		}
	}

	// check for system keys, etc
	// caps lock
	if(key_states[0x39] >= 1)	// caps lock pressed
	{
		if(caps_lock)		// lock already on
		{
			// disable it
			caps_lock = 0;
			to_return = to_return | ((uint64_t)1 << 58);
		}
		else
		{
			// enable it
			caps_lock = 1;
			to_return = to_return | ((uint64_t)1 << 61);
		}
		key_states[0x39] = 0;
	}

	// num lock
	/*
	if(key_states[0x53] >= 1)
	{
		if(num_lock)
		{
			// disable it
			num_lock = 0;
			to_return = to_return | ((uint64_t)1 << 59);
		}
		else
		{
			// enable it
			num_lock = 1;
			to_return = to_return | ((uint64_t)1 << 62);
		}
		key_states[0x39] = 0;
	}

	// scroll lock
	if(key_states[0x47] >= 1)
	{
		if(scroll_lock)
		{
			// disable it
			scroll_lock = 0;
			to_return = to_return | ((uint64_t)1 << 60);
		}
		else
		{
			// enable it
			scroll_lock = 1;
			to_return = to_return | ((uint64_t)1 << 63);
		}
		key_states[0x47] = 0;
	}*/

	// check for F1 key
	if(key_states[0x3a])
	{
		if(!F1)
		{
			F1 = 1;
		}
	}
	else
	{
		if(F1)
		{
			// key was released
			to_return = to_return | 1;

			// release F1
			F1 = 0;
		}
	}

	// check for super key
	if(key_states[0xe3] | key_states[0xe0])	// CHANGE TO JUST SUPER LATER (0xe3 only)
	{
		if(!super_pressed)
		{
			super_pressed = 1;
		}
	}
	else
	{
		if(super_pressed)
		{
			// key was released
			super_pressed = 0;

			// check for do not signal
			if(!super_do_not_signal)
			{
				to_return = to_return | 2;
			}

			// reset do not signal
			super_do_not_signal = 0;
		}
	}

	// check for super + key combination released
	if(super_pressed)
	{
		// check left arrow
		if(key_states[0x50])
		{
			if(!a_left)
			{
				a_left = 1;
			}
		}
		else
		{
			if(a_left)
			{
				// key was released
				a_left = 0;
				to_return = to_return | (1 << 2);
				super_do_not_signal = 1;
			}
		}

		// check right arrow
		if(key_states[0x4f])
		{
			if(!a_right)
			{
				a_right = 1;
			}
		}
		else
		{
			if(a_right)
			{
				// key was released
				a_right = 0;
				to_return = to_return | (1 << 3);
				super_do_not_signal = 1;
			}
		}

		// check up arrow
		if(key_states[0x52])
		{
			if(!a_up)
			{
				a_up = 1;
			}
		}
		else
		{
			if(a_up)
			{
				// key was released
				a_up = 0;
				to_return = to_return | (1 << 4);
				super_do_not_signal = 1;
			}
		}

		// check down arrow
		if(key_states[0x51])
		{
			if(!a_down)
			{
				a_down = 1;
			}
		}
		else
		{
			if(a_down)
			{
				// key was released
				a_down = 0;
				to_return = to_return | (1 << 5);
				super_do_not_signal = 1;
			}
		}

		// check c key
		if(key_states[0x06])
		{
			if(!c_down)
			{
				c_down = 1;
			}
		}
		else
		{
			if(c_down)
			{
				// key was released
				c_down = 0;
				to_return = to_return | (1 << 9);
				super_do_not_signal = 1;
			}
		}

		// check l key
		if(key_states[0x0f])
		{
			if(!l_down)
			{
				l_down = 1;
			}
		}
		else
		{
			if(l_down)
			{
				// key was released
				l_down = 0;
				to_return = to_return | (1 << 10);
				super_do_not_signal = 1;
			}
		}
	}

	// check if super is held
	if(super_pressed)
	{
		to_return = to_return | ((uint64_t)1 << 57);
	}

	return to_return;
}

// function to get a formatted ASCII char from the keystate
char GetASCIIFromKeyState()
{
	// go through each key and check if the state is between 1 and 3 or above 10
	// then check if shift key is pressed
	// then fetch the ascii code for that key
	int i;
	for(i = 0; i < 128; i++)
	{
		if((key_states[i] >= 1 && key_states[i] <= 2) || key_states[i] > 20)
		{
			break;
		}
	}

	// check if shift keys are pressed
	if(key_states[0xe1] || key_states[0xe5])
	{
		shift = 1;
	}
	else
	{
		shift = 0;
	}

	// check state of shift and caps lock
	// shift + caps lock = normal letters + symbols
	// shift = cap letters + symbols
	// cap = cap letters + numbers
	// none = normal
	char to_return = 0;
	if(shift && caps_lock)
	{
		to_return = USB_ASCII_CAPS_SHIFT[i];
	}
	else if(shift)
	{
		to_return = USB_ASCII_SHIFT[i];
	}
	else if(caps_lock)
	{
		to_return = USB_ASCII_CAPS[i];
	}
	else
	{
		to_return = USB_ASCII_NO_SHIFT[i];
	}

	return to_return;
}

// function to dump the 256-byte keystate to the destination address
void GetKeyState(uint8_t *destination_key_state)
{
	int i;
	uint64_t *dest = (uint64_t*)destination_key_state;
	uint64_t *src = (uint64_t*)key_states;
	for(i = 0; i < 32; i++)
	{
		dest[i] = src[i];
	}
	return;
}













